import { KVAny, KV } from "./kv";
import { Fun1 } from "./fun";

export interface FlatTree extends KVAny {
  id: string | number;
  pId: string | number;
  [key: string]: any;
}

export interface Tree {
  [key: string]: any;
  children: Tree[];
}

export class SimpleTree implements Tree, FlatTree {
  static readonly id = "id";
  static readonly pId = "pId";
  static readonly value = "value";

  [key: string]: any;
  id: string | number;
  pId: string | number;
  children: SimpleTree[] = [];

  constructor(properties: KVAny, children?: SimpleTree[]) {
    Object.entries(properties).forEach((entry) => (this[entry[0]] = entry[1]));
    if (children) {
      this.children = children;
    }
  }

  static fromFlatTree(
    flatTreeList: FlatTree[],
    rootId = 0,
    propertyName = [SimpleTree.value]
  ): SimpleTree[] {
    const result: SimpleTree[] = [];
    const nodeSet = new Set(flatTreeList);
    const currentLevelNodeSet = new Set<FlatTree>();

    nodeSet.forEach((node) => {
      if (+node[SimpleTree.pId] === rootId) {
        currentLevelNodeSet.add(node);
        nodeSet.delete(node);
      }
    });

    currentLevelNodeSet.forEach((node) => {
      const properties = {};
      [...propertyName, SimpleTree.id, SimpleTree.pId].forEach((property) => {
        properties[property] = node[property];
      });
      result.push(
        new SimpleTree(
          properties,
          SimpleTree.fromFlatTree(
            [...nodeSet],
            +node[SimpleTree.id],
            propertyName
          )
        )
      );
    });

    return result;
  }

  static toFlatTree(
    simpleTreeList: SimpleTree[],
    propertyName = [SimpleTree.value]
  ): FlatTree[] {
    const result: FlatTree[] = [];

    simpleTreeList.forEach((simpleTree) => {
      const flatTree = {};
      [...propertyName, SimpleTree.id, SimpleTree.pId].forEach(
        (property) => (flatTree[property] = simpleTree[property])
      );
      result.push(flatTree as FlatTree);

      if (simpleTree.children.length > 0) {
        result.push(
          ...SimpleTree.toFlatTree(simpleTree.children, propertyName)
        );
      }
    });

    return result;
  }

  static setLeafFlag(simpleTreeList: SimpleTree[], leafName = "isLeaf") {
    simpleTreeList.forEach((simpleTree) => {
      if (simpleTree.children && simpleTree.children.length === 0) {
        simpleTree[leafName] = true;
      } else {
        SimpleTree.setLeafFlag(simpleTree.children, leafName);
      }
    });
  }

  static updatePropertyValue(
    simpleTreeList: SimpleTree[],
    properties: KV<Fun1<any>>
  ) {
    simpleTreeList.forEach((simpleTree) => {
      Object.entries(properties).forEach(
        (entry) => (simpleTree[entry[0]] = entry[1](simpleTree[entry[0]]))
      );
      if (simpleTree.children) {
        this.updatePropertyValue(simpleTree.children, properties);
      }
    });
  }

  static updatePropertyName(simpleTreeList: any[], properties: KVAny) {
    simpleTreeList.forEach((simpleTree) => {
      Object.entries(properties).forEach(
        (entry) => (simpleTree[entry[1]] = simpleTree[entry[0]])
      );
      if (simpleTree.children) {
        this.updatePropertyName(simpleTree.children, properties);
      }
    });
  }

  static updateTreeName(simpleTreeList: SimpleTree[]) {
    simpleTreeList.map((item) => {
      item.title = item.name;
      item.key = item.id;
      item.expanded = true;
      if (item.children) {
        this.updateTreeName(item.children);
      } else {
        item.isLeaf = true;
      }
    });
  }
}
